﻿using Cache.RedisDistributed.Contexts;
using Cache.RedisDistributed.Locks;
using Cache.RedisDistributed.Modules;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Cache.RedisDistributed.Controllers
{
    /// <summary>
    /// 商品控制器
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class ProductController : ControllerBase
    {

        private readonly ILogger<ProductController> _logger;
        private readonly ProductDbContext _productDbContext;
        private readonly IMemoryCache memoryCache;
        private readonly ConnectionMultiplexer _connectionMultiplexer;

        public ProductController(ILogger<ProductController> logger,
                                 ProductDbContext productDbContext,
                                 ConnectionMultiplexer connectionMultiplexer)
        {
            _logger = logger;
            _productDbContext = productDbContext;
            _connectionMultiplexer = connectionMultiplexer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [HttpGet("StringTest")]
        public string RedisStringTest()
        {
            //1、设置一个String的值: StringSet
            var _db = _connectionMultiplexer.GetDatabase(0);
            var res = _db.StringSet("key1", "value01");
            Console.WriteLine($"setStringResult:{res},GetResult=>key1: {_db.StringGet("key1")}");
            var res1 = _db.StringGetSet("key2", "10000056+989321");
            Console.WriteLine($"StringGetSet:{res1}");

            #region StringSetRange
            /*StringSetRange 方法是 IDatabase 接口中的一个方法，它允许你在 Redis 字符串的指定范围内设置值。
                StringSetRange 方法的签名如下：
               【bool StringSetRange(RedisKey key, long offset, string value);】
                    key: Redis 键的名称。
                    offset: 从哪个位置开始替换字符串。
                    value: 要设置的新字符串值。
                这个方法会返回一个布尔值，表示操作是否成功。
            */

            //2、大字符串追加  _db.StringSetRange(key，原始字符串长度，新字符串
            string key = "mykey";
            string initialValue = "Hello, ";
            bool success = _db.StringSet(key, initialValue);
            if (success)
            {
                string newValue = "world!";
                var appendResult = _db.StringSetRange(key, initialValue.Length, newValue);
                Console.WriteLine(appendResult);
            }

            #endregion

            #region StringGetRange

            //3、从指定位置开始读取（包含起始索引，不包含结束索引）  
            string val1 = _db.StringGet("key2").ToString();
            string val2 = _db.StringGetRange("key2", 9, val1.Length);
            Console.WriteLine("val2", val2);


            #endregion

            //4、StringGetWithExpiry
            //:获取字符串的值以及key的剩余的过期时间
            var key2Expiry = _db.StringGetWithExpiry("key2");
            Console.WriteLine($"key2Expiry:{key2Expiry}");
            //5、StringGetSet
            //:获取原来的String值 并用新值替换
            var key2GetSetRes = _db.StringGetSet("key2", "999988855522");
            Console.WriteLine($"key2GetSetRes:{key2GetSetRes}");

            //6、StringAppend
            //字符串追加，返回追加后的字符串长度。
            var key1Append = _db.StringAppend("key1", "0000000111111110000000000");
            Console.WriteLine($"key1Append:{key1Append}");

            //7、StringLength
            //获取字符串的长度
            var key1Len = _db.StringLength("key1");
            Console.WriteLine($"key1Len:{key1Len}");

            //8、StringIncrement 累加运算，返回累加后的数值
            //字符串为一个数值的，使用此函数可以做加法运算，如果存储的不是一个数值，将会引发错误
            var key3Val = _db.StringSet("key3", "10");
            var res3 = _db.StringIncrement("key3", 100.101);
            Console.WriteLine($"StringIncrement:res3={res3}");

            //9、StringDecrement减法运算
            var res4 = _db.StringDecrement("key3", 10.1);
            Console.WriteLine($"StringDecrement:res4={res4}");

            //10、StringGetBit 获取某位二进制是不是1
            var res5 = _db.StringGetBit("key3", 3);
            Console.WriteLine($"StringGetBit:res5={res5}");

            //11、 StringBitCount,设置某位二进制，返回二进制中原来的值

            //12、StringSetBit获取范围中二进制，出现1的次数

            return string.Empty;
        }


        /// <summary>
        /// GetHashSetValue
        /// </summary>
        /// <returns></returns>
        [HttpGet("SaveData")]
        public Product GetHashSetValue()
        {
            var _db = _connectionMultiplexer.GetDatabase(0);
            // 1、查询数据库数据
            Product product = _productDbContext.Products.FirstOrDefault(s => s.Id == 1);
            #region 1、存储商品对象-字典形式
            {
                //string ProductSold = _connectionMultiplexer.GetDatabase(0).HashGet("productHash", "ProductSold");
                //if (string.IsNullOrEmpty(ProductSold))
                //{
                //    product = _productDbContext.Products.FirstOrDefault(s => s.Id == 1);
                //    _connectionMultiplexer.GetDatabase(0).HashSet("productHash", "ProductSold", product.ProductStock);
                //    //设置Key过期时间
                //    _connectionMultiplexer.GetDatabase(0).KeyExpire("productHash", TimeSpan.FromSeconds(10));
                //}
                //// 1、增加销量,如果Key不存在则会创建
                //_connectionMultiplexer.GetDatabase(0).HashIncrement("productHash", "ProductSold");
                //return product;
            }
            #endregion

            #region 2、存储商品对象-事务

            //string ProductSold = _connectionMultiplexer.GetDatabase(0).HashGet("productHash", "ProductSold");
            //string ProductStock = _connectionMultiplexer.GetDatabase(0).HashGet("productHash", "ProductStock");
            //if (string.IsNullOrEmpty(ProductStock))
            //{
            //    product = _productDbContext.Products.FirstOrDefault(s => s.Id == 1);

            //    //创建事务
            //    ITransaction transaction = _connectionMultiplexer.GetDatabase(0).CreateTransaction();
            //    //transaction.AddCondition(Condition.HashEqual("productHash", "ProductSold", product.ProductSold));//乐观锁

            //    transaction.HashSetAsync("productHash", "ProductSold", product.ProductSold);
            //    transaction.HashSetAsync("productHash", "ProductStock", product.ProductStock);
            //    transaction.HashSetAsync("productHash", "ProductSold", 100); // 修改原油值
            //    transaction.HashSetAsync("productHash", "ProductStock2", "adc测试");
            //    transaction.HashSetAsync("productHash", "ProductStock4", DateTime.Now.ToString("f"));
            //    transaction.HashSetAsync("productHash", "ProductStock5", "http://www.bing.com/a/b/c/d/111");
            //    bool commit = transaction.Execute();
            //    // commit 为true：那么就是提交成功 如果为false,提交失败
            //    if (commit)
            //    {
            //        Console.WriteLine("提交成功");

            //        var productSoldVal =  transaction.HashGetAsync("productHash", "ProductSold");
            //        bool commit2 = transaction.Execute(); //如果不执行Execute，将不会得到ProductSold的值，并且该线程会被锁定。
            //        Console.WriteLine("-----------------");
            //        Console.WriteLine(productSoldVal.Result.ToString());
            //    }
            //    else
            //    {
            //        Console.WriteLine("回滚成功");
            //    }
            //}
            //return product;
            #endregion

            #region 3、存储商品对象-批量操作
            {
                //var batch = _connectionMultiplexer.GetDatabase(0).CreateBatch();
                //List<Product> products = new List<Product>();
                //// 2、从数据库中查询(模拟5条数据）
                //products = _productDbContext.Products.Take(5).ToList();
                //// 3、存储到redis中
                //for (int i = 0; i < products.Count; i++)
                //{
                //    batch.HashSetAsync("productHash" + i, "ProductSold", products[i].ProductSold);
                //}
                //batch.Execute();
                //return product;
                ////数据库中会生成productHash0 ~ productHash4共5个Key
            }
            #endregion


            #region 4、存储商品对象-消息队列-扩展
            {
            }
            #endregion

            #region 5、存储商品对象-订阅发布-扩展
            {
            }
            #endregion

            #region 6、存储商品对象-分布式锁
            {

            }
            #endregion

            #region 7、存储商品对象-集群
            {

            }
            #endregion

            return product;
        }



        [HttpGet("GetPages")]
        public List<Product> SetMmebersProduct()
        {
            var _db = _connectionMultiplexer.GetDatabase(0);
            List<Product> product = _productDbContext.Products.Take(100).ToList();

            #region 1、存储商品对象-集合商品数据
            {
                //// 1、从redis中取对象
                //RedisValue[] productvalues = _connectionMultiplexer.GetDatabase(0).SetMembers("products");
                //List<Product> products = new List<Product>();
                //if (productvalues.Length == 0)
                //{
                //    // 2、从数据库中查询
                //    products = _productDbContext.Products.ToList();

                //    // 3、存储到redis中
                //    List<RedisValue> redisValues = new List<RedisValue>();
                //    foreach (var product1 in products)
                //    {
                //        string productjson = JsonConvert.SerializeObject(product1);//序列化
                //        redisValues.Add(productjson);
                //    }

                //    _connectionMultiplexer.GetDatabase(0).SetAdd("products", redisValues.ToArray());

                //    return products;
                //}

                //// 4、序列化，反序列化
                //foreach (var redisValue in productvalues)
                //{
                //    var product1 = JsonConvert.DeserializeObject<Product>(redisValue);//反序列化
                //    products.Add(product1);
                //}
                //return product;
            }
            #endregion

            #region 2、有序集合（Sorted Set）分页
            {

                // 假设有如下产品数据及其评分
                Dictionary<string, double> products = new Dictionary<string, double>()
                {
                    { "product1", 4.5 },
                    { "product2", 3.8 },
                    { "product3", 4.9 },
                    // 更多产品...
                };

                // 将产品及其评分存入有序集合
                foreach (var product2 in products)
                {
                    _db.SortedSetAdd("sortedProducts", product2.Key, product2.Value);
                }


                // 假设每页大小为10条数据
                int pageSize = 10;
                long page = 1; // 当前页码，从1开始计数
                double minScore = double.NegativeInfinity;
                double maxScore = double.PositiveInfinity;

                // 计算分页的起始和结束得分
                double fromScore = (page - 1) * pageSize;
                double toScoreExclusive = page * pageSize;

                // 使用SortedSetRangeByScore获取当前页数据
                var itemsOnPage = _db.SortedSetRangeByScore("sortedProducts", fromScore, toScoreExclusive, exclude: Exclude.None);

                foreach (var item in itemsOnPage)
                {
                    Console.WriteLine($"Item: {item}");
                }

                // 注意：实际应用中，minScore和maxScore可能由业务逻辑确定，而非简单的页码转换而来

                // 清理资源
                _connectionMultiplexer.Close();
                _connectionMultiplexer.Dispose();
            }
            #endregion
            return product;
        }


        /// <summary>
        /// 排序存储
        /// </summary>
        /// <returns></returns>
        [HttpGet("SetSortedAdd")]
        public List<Product> SetSortedAdd()
        {
            #region 存储商品对象-集合
            {
                // 1、从redis中取对象
                IDatabase _db = _connectionMultiplexer.GetDatabase(0);
                var productvalues = _db.SetMembers("sortSetProducts");

                List<Product> products = new List<Product>();
                if (productvalues.Length == 0)
                {
                    //2、缓存Redis没有数据，从数据库查出数据
                    products = _productDbContext.Products.OrderByDescending(it => it.ProductSold).ToList();
                    //3、存储到redis中
                    List<RedisValue> redisValues = new List<RedisValue>();
                    foreach (var product1 in products)
                    {
                        string productjson = JsonConvert.SerializeObject(product1);//序列化
                        redisValues.Add(productjson);
                    }
                    //这种方式保存需要存储之前排好序。不利于当个插入。
                    _db.SetAdd("sortSetProducts", redisValues.ToArray());
                    return products;

                }

                // 缓存中有数据则返回
                foreach (var redisValue in productvalues)
                {
                    Product product2 = JsonConvert.DeserializeObject<Product>(redisValue);//反序列化
                    products.Add(product2);
                }
                return products;

            }
            #endregion

        }

        /// <summary>
        /// 排序存储
        /// </summary>
        /// <returns></returns>
        [HttpGet("SetSortedAdd2")]
        public List<Product> SetSortedAdd2()
        {
            #region 存储商品对象-集合
            {
                // 1、从redis中取对象
                IDatabase _db = _connectionMultiplexer.GetDatabase(0);
                var productvalues = _db.SortedSetScan("sortProducts").ToArray();

                List<Product> products = new List<Product>();
                if (productvalues.Length == 0)
                {
                    //2、缓存Redis没有数据，从数据库查出数据
                    products = _productDbContext.Products.OrderByDescending(it => it.ProductSold).ToList();
                    //3、存储到redis中
                    List<RedisValue> redisValues = new List<RedisValue>();
                    foreach (var product1 in products)
                    {
                        string productjson = JsonConvert.SerializeObject(product1);//序列化
                        redisValues.Add(productjson);

                        // 对商品数据进行排序保存
                        _db.SortedSetAdd("sortProducts2", productjson, product1.ProductSort);
                    }
                    return products;

                }

                // 缓存中有数据则返回
                foreach (var redisValue in productvalues)
                {
                    Product product2 = JsonConvert.DeserializeObject<Product>("1111111111");//反序列化
                    products.Add(product2);
                }
                return products;

            }
            #endregion
        }


        /// <summary>
        /// 并发测试-下单-扣减库存
        /// </summary>
        /// <returns></returns>
        [HttpGet("SubStock")]
        public IActionResult SubStock()
        {
            #region 1、扣减库存流程
            {

                RedisLock redisLock = new RedisLock();
                redisLock.Lock();
                // 1、获取商品库存
                var stocks = getPorductStocks();

                // 2、判断商品库存是否为空
                if (stocks.Count == 0)
                {
                    // 2.1 秒杀失败消息
                    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}：秒杀已结束，商品编号:{stocks.Count}");
                    redisLock.UnLock();
                    return new JsonResult("秒杀失败");
                }

                // 3、秒杀成功消息
                Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}：恭喜你，秒杀成功，商品编号:{stocks.Count}");

                // 4、扣减商品库存
                subtracProductStocks(stocks);
                redisLock.UnLock();
                return new JsonResult("秒杀成功");
            }

            #endregion
        }

        /// <summary>
        /// 获取商品库存
        /// </summary>
        /// <returns></returns>
        private Stocks getPorductStocks()
        {
            // 1、查询数据库获取库存，获取第一个商品的库存数(1)
            Stocks stocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == 1);
            // 2、返回库存
            return stocks;
        }

        /// <summary>
        /// 扣减商品库存
        /// </summary>
        private void subtracProductStocks(Stocks stocks)
        {
            // 1、扣减商品库存
            Stocks updateStocks = _productDbContext.Stocks.FirstOrDefault(s => s.Id == stocks.Id);
            updateStocks.Count = stocks.Count - 1;

            // 2、更新数据库
            _productDbContext.SaveChanges();
        }

    }
}
